
package com.anzym.carffcastsample;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.MediaRouteActionProvider;
import android.support.v7.media.MediaRouteSelector;
import android.support.v7.media.MediaRouter;
import android.support.v7.media.MediaRouter.Callback;
import android.support.v7.media.MediaRouter.RouteInfo;
import android.text.Html;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;

import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;

import com.google.android.gms.cast.ApplicationMetadata;
import com.google.android.gms.cast.Cast;
import com.google.android.gms.cast.Cast.Listener;
import com.google.android.gms.cast.CastDevice;
import com.google.android.gms.cast.CastMediaControlIntent;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaStatus;
import com.google.android.gms.cast.RemoteMediaPlayer;
//import com.google.android.gms.cast.metadata.MediaMetadata;
//import com.google.android.gms.cast.metadata.MovieMetadata;

import java.io.IOException;

public class MainActivity extends ActionBarActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
    protected Context mContext;
    private Button bbbButton;
    private Button hlsButton;
    private Button ssButton;
    private Button dashButton;
    private ImageButton mPlayPauseButton;
    private ImageButton mStopButton;
    private boolean mPlayButtonShowsPlay = false;
    private boolean mVideoIsStopped = false;

    private double mVolumeIncrement = 0.05;

    private TextView mStatusText;
    private TextView mCurrentlyPlaying;

    private String mAppId;

    private MediaRouter mMediaRouter;
    private MediaRouteSelector mMediaRouteSelector;

    private CastDevice mSelectedDevice;
    private GoogleApiClient mApiClient;

    private MyMediaRouterCallback mMediaRouterCallback;
    private ConnectionCallbacks mConnectionCallbacks;
    private ConnectionFailedListener mConnectionFailedListener;
    private Cast.Listener mCastListener;
    private boolean mApplicationStarted;
    private RemoteMediaPlayer mRemoteMediaPlayer;
    private MediaStatus mStatus;
    private MediaInfo mInfo;
    private MediaMetadata mData;
    private boolean mWaitingForReconnect;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = getApplicationContext();
        mAppId = getResources().getString(R.string.app_id);
        mWaitingForReconnect = false;
        mMediaRouter = MediaRouter.getInstance(getApplicationContext());
        mMediaRouteSelector = new MediaRouteSelector.Builder()
                .addControlCategory(CastMediaControlIntent
                        .categoryForCast(mAppId)).build();
        mMediaRouterCallback = new MyMediaRouterCallback();
        mConnectionCallbacks = new ConnectionCallbacks();
        mConnectionFailedListener = new ConnectionFailedListener();

        bbbButton = (Button) findViewById(R.id.bbb_button);
        hlsButton = (Button) findViewById(R.id.hls_button);
        ssButton = (Button) findViewById(R.id.ss_button);
        dashButton = (Button) findViewById(R.id.dash_button);

        mStatusText = (TextView) findViewById(R.id.play_status_text);
        mCurrentlyPlaying = (TextView) findViewById(R.id.currently_playing);
        mCurrentlyPlaying.setText(getString(R.string.tap_to_select));

        mPlayPauseButton = (ImageButton) findViewById(R.id.play_pause_button);
        mStopButton = (ImageButton) findViewById(R.id.stop_button);
        Log.d(TAG, "initButtions");
        initButtons();

        Thread myThread = null;
        Runnable runnable = new StatusRunner();
        myThread = new Thread(runnable);
        Log.d(TAG, "Starting statusRunner thread");
        myThread.start();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.main, menu);
        MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item);
        MediaRouteActionProvider mediaRouteActionProvider =
                (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem);
        mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector);
        return true;
    }

    @Override
    protected void onResume() {
        super.onResume();
        mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback,
                MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN);
    }

    @Override
    protected void onPause() {
        if (isFinishing()) {
            mMediaRouter.removeCallback(mMediaRouterCallback);
        }
        super.onPause();
    }

    /**
     * Initializes all buttons by adding user controls and listeners.
     */
    public void initButtons() {
        Log.d(TAG, "Initializing Buttons");
        bbbButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                loadMedia(0);
            }
        });
        hlsButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                loadMedia(1);
            }
        });
        ssButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                loadMedia(2);
            }
        });
        dashButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                loadMedia(3);
            }
        });
        
        mPlayPauseButton.setImageResource(R.drawable.pause_button);
        mPlayPauseButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "CLICKED PLAY-PAUSE");
                onPlayClicked(!mPlayButtonShowsPlay);
            }
        });
        mStopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d(TAG, "STOP");
                onStopClicked();
            }
        });
        // mCurrentlyPlaying.setOnClickListener(new View.OnClickListener() {
        // @Override
        // public void onClick(View v) {
        // Log.d(TAG, "Selecting Media");
        // mMediaSelectionDialog.setTitle(getResources().getString(
        // R.string.medial_dialog_title));
        // mMediaSelectionDialog.show();
        // }
        // });
    }

    /**
     * Plays or pauses the currently loaded media, depending on the current
     * state of the <code>
     * mPlayPauseButton</code>.
     * 
     * @param playState indicates that Play was clicked if true, and Pause was
     *            clicked if false
     */
    public void onPlayClicked(boolean playState) {
        if (playState) {
            try {
                if (mRemoteMediaPlayer != null) {
                    mRemoteMediaPlayer.pause(mApiClient);
                } else {
                    Log.e(TAG, "onClick-Play - mRemoteMediaPlayer==null");
                }
            } catch (IOException e) {
                Log.e(TAG, "Failed to send stop comamnd.");
            }
            mPlayPauseButton.setImageResource(R.drawable.play_button);
        } else {
            try {
                if (mRemoteMediaPlayer != null) {
                    if (mVideoIsStopped) {
                        mRemoteMediaPlayer.play(mApiClient);
                        mVideoIsStopped = !mVideoIsStopped;
                    } else {
                        mRemoteMediaPlayer.play(mApiClient);
                    }
                } else {
                    Log.e(TAG, "onClick-Play - mMPMS==null");
                }
            } catch (IOException e) {
                Log.e(TAG, "Failed to send play/resume comamnd.");
            }
            mPlayPauseButton.setImageResource(R.drawable.pause_button);
        }
        mPlayButtonShowsPlay = !mPlayButtonShowsPlay;
    }

    /**
     * Handles stopping the currently playing media upon the stop button being
     * pressed.
     */
    public void onStopClicked() {
        try {
            if (mRemoteMediaPlayer != null) {
                mRemoteMediaPlayer.stop(mApiClient);
                mVideoIsStopped = !mVideoIsStopped;
                mPlayPauseButton.setImageResource(R.drawable.play_button);
                mPlayButtonShowsPlay = true;
            } else {
                Log.e(TAG, "onStopClicked - mRemoteMediaPlayer==null");
            }
        } catch (IOException e) {
            Log.e(TAG, "Failed to send pause comamnd.");
        }
    }

    /**
     * Processes volume up and volume down actions upon receiving them as key
     * events.
     */
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (mVolumeIncrement == Float.MIN_VALUE)
            return false;
        if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
            incrementVolume(mVolumeIncrement);
            Log.i(TAG, "upVolume");
        } else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            incrementVolume(-mVolumeIncrement);
            Log.i(TAG, "downVolume");
        } else {
            return super.onKeyDown(keyCode, event);
        }
        return true;
    }

    public void incrementVolume(double delta) {
        Log.i(TAG, "delta: " + delta);
        double vol = Cast.CastApi.getVolume(mApiClient);
        if (vol >= 0) {
            try {
                Log.i(TAG, "Setting volume to: " + (vol + delta));
                Cast.CastApi.setVolume(mApiClient, vol + delta);
                // updateVolume((int) (vol + delta));
            } catch (IllegalArgumentException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalStateException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    public void updateVolume(int i) {
        if (null != mMediaRouter.getSelectedRoute()) {
            RouteInfo info = mMediaRouter.getSelectedRoute();
            info.requestUpdateVolume(i);
        }
    }

    /**
     * Start the receiver app
     */
    private void launchReceiver() {
        Log.i(TAG, "Launching Receiver");
        try {
            Log.i(TAG, "Creating new CastListner");
            mCastListener = new Cast.Listener() {
                @Override
                public void onApplicationStatusChanged() {
                    if (mApiClient != null) {
                        Log.d(TAG, "onApplicationStatusChanged: "
                                + Cast.CastApi.getApplicationStatus(mApiClient));
                        
                    }
                    Log.d(TAG, "mStatus onAppstatusChanged");
                }

                @Override
                public void onVolumeChanged() {
                    if (mApiClient != null) {
                        Log.d(TAG,
                                "onVolumeChanged: "
                                        + Cast.CastApi.getVolume(mApiClient));
                    }
                }

                @Override
                public void onApplicationDisconnected(int errorCode) {
                    Log.d(TAG, "application has stopped");
                    teardown();
                }
            };
            Log.i(TAG, "ConnectionCallback");
            mConnectionCallbacks = new ConnectionCallbacks();
            Log.i(TAG, "ConnectionFailedListener");
            mConnectionFailedListener = new ConnectionFailedListener();
            Log.i(TAG, "CastOptions");

            Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions
                    .builder(mSelectedDevice, mCastListener);
            Log.i(TAG, "ApiClient");
            mApiClient = new GoogleApiClient.Builder(this)
                    .addApi(Cast.API, apiOptionsBuilder.build())
                    .addConnectionCallbacks(mConnectionCallbacks)
                    .addOnConnectionFailedListener(mConnectionFailedListener)
                    .build();
            Log.i(TAG, "ApiClientConnect");
            mApiClient.connect();
        } catch (Exception e) {
            Log.e(TAG, "Failed launchReceiver", e);
        }
    }

    private class MyMediaRouterCallback extends MediaRouter.Callback {

        @Override
        public void onRouteSelected(MediaRouter router, RouteInfo info) {
            mSelectedDevice = CastDevice.getFromBundle(info.getExtras());
            String routeId = info.getId();
            Log.d(TAG, "onRouteSelected - Selected Device: " + routeId);
            launchReceiver();
        }

        @Override
        public void onRouteUnselected(MediaRouter router, RouteInfo info) {
            teardown();
            String routeId = info.getId();
            Log.d(TAG, "onRouteUnselected - Disconnected from: " + routeId);
            mSelectedDevice = null;
        }
    }

    private class ConnectionCallbacks implements
            GoogleApiClient.ConnectionCallbacks {
        @Override
        public void onConnected(Bundle connectionHint) {
            if (mWaitingForReconnect) {
                mWaitingForReconnect = false;
                // <TODO> implement this method
                // reconnectChannels();
             // Check if the receiver app is still running
                if ((connectionHint != null)
                        && connectionHint.getBoolean(Cast.EXTRA_APP_NO_LONGER_RUNNING)) {
                    Log.d(TAG, "App  is no longer running");
                    teardown();
                } else {
                    // Re-connect the media channel
                    try {
                        Log.i(TAG, "Attempting to REconnect media Channel");
                        Cast.CastApi.setMessageReceivedCallbacks(mApiClient,
                                mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
                    } catch (IOException e) {
                        Log.e(TAG, "Exception while creating channel", e);
                    }
                }
            } else {
                try {
                    Cast.CastApi.launchApplication(mApiClient, mAppId, false)
                            .setResultCallback(
                                    new ResultCallback<Cast.ApplicationConnectionResult>() {
                                        @Override
                                        public void onResult(Cast.ApplicationConnectionResult result) {
                                            Status status = result.getStatus();
                                            Log.d(TAG, "ApplicationConnectionResultCallback.onResult: statusCode"
                                                            + status.getStatusCode());

                                            if (status.isSuccess()) {
                                                Log.d(TAG, "Successfully connected");
                                                ApplicationMetadata applicationMetadata =
                                                        result.getApplicationMetadata();
                                                String sessionId = result.getSessionId();
                                                Log.d(TAG, "Session id: " + sessionId);
                                                String applicationStatus = result
                                                        .getApplicationStatus();
                                                Log.d(TAG, "App Status " + applicationStatus);
                                                mApplicationStarted = true;
                                                //boolean wasLaunched = result.getWasLaunched();
                                                mRemoteMediaPlayer = new RemoteMediaPlayer();
                                                mRemoteMediaPlayer
                                                        .setOnStatusUpdatedListener(new RemoteMediaPlayer.OnStatusUpdatedListener() {

                                                            @Override
                                                            public void onStatusUpdated() {
                                                                mStatus = mRemoteMediaPlayer.getMediaStatus();
                                                                boolean isPlaying = mStatus.getPlayerState() == MediaStatus.PLAYER_STATE_PLAYING;

                                                            }
                                                        });
                                                mRemoteMediaPlayer
                                                        .setOnMetadataUpdatedListener(new RemoteMediaPlayer.OnMetadataUpdatedListener() {

                                                            @Override
                                                            public void onMetadataUpdated() {
                                                                mInfo = mRemoteMediaPlayer.getMediaInfo();
                                                                mData = mInfo.getMetadata();
                                                            }
                                                        });
                                                try {
                                                    Log.i(TAG, "Attempting to CONNECT media Channel");
                                                    Cast.CastApi.setMessageReceivedCallbacks(mApiClient,
                                                            mRemoteMediaPlayer.getNamespace(), mRemoteMediaPlayer);
                                                } catch (IOException e) {
                                                    Log.e(TAG, " Exception while creating Media Channel", e);
                                                }
                                            } else {
                                                teardown();
                                            }
                                        }
                                    });
                } catch (Exception e) {
                    Log.e(TAG, "Failed to launch application", e);
                }
            }
        }

        @Override
        public void onConnectionSuspended(int cause) {
            // <TODO>
            Log.i(TAG, "ConnectionSuspened");
            mWaitingForReconnect = true;
        }
    }

    private class ConnectionFailedListener implements
            GoogleApiClient.OnConnectionFailedListener {

        @Override
        public void onConnectionFailed(ConnectionResult result) {
            Log.e(TAG, "ConnectionFailed");
            teardown();
        }
    }

    /**
     * load the media (play the movie)
     */
    private void loadMedia(int i) {
        MediaMetadata mediaMetadata = new MediaMetadata();
        MediaInfo mediaInfo = null;
        if (i == 0) {
            mediaMetadata.putString(MediaMetadata.KEY_TITLE, "Big Buck Bunny");
            mediaInfo = new MediaInfo.Builder(
                "http://commondatastorage.googleapis.com/gtv-videos-bucket/big_buck_bunny_1080p.mp4")
                .setContentType("video/mp2t")
                .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
                .setMetadata(mediaMetadata)
                .build();
        } else if (i == 1) {
            mediaMetadata.putString(MediaMetadata.KEY_TITLE, "HLS Sample");
            mediaInfo = new MediaInfo.Builder(
                "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8")
                .setContentType("video/mp4")
                .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
                .setMetadata(mediaMetadata)
                .build();
        }else if (i == 2) {
            mediaMetadata.putString(MediaMetadata.KEY_TITLE, "Super Speedway");
            mediaInfo = new MediaInfo.Builder(
                "http://playready.directtaps.net/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/Manifest")
                .setContentType("application/vnd.ms-sstr+xml")
                .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
                .setMetadata(mediaMetadata)
                .build();
        } else if (i == 3) {
            mediaMetadata.putString(MediaMetadata.KEY_TITLE, "Dash Sample");
            mediaInfo = new MediaInfo.Builder(
                "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car-20120827-manifest.mpd")
                .setContentType("application/dash+xml")
                .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
                .setMetadata(mediaMetadata)
                .build();
        }
        try {
            mRemoteMediaPlayer.load(mApiClient, mediaInfo, true);
        } catch (IllegalStateException e) {
            Log.e(TAG, "Problem occurred with media during loading", e);
        }

    }

    /**
     * Tear down the connection to the receiver
     */
    private void teardown() {
        Log.d(TAG, "Tearing Down");
        if (mApiClient != null) {
            if (mApplicationStarted) {
                Cast.CastApi.stopApplication(mApiClient);
                mApplicationStarted = false;
            }
            if (mApiClient.isConnected()) {
                mApiClient.disconnect();
            }
            mApiClient = null;
        }
        mSelectedDevice = null;
    }

    /**
     * Updates the status of the currently playing video in the dedicated
     * message view.
     */
    public void updateStatus() {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                try {
                    updateCurrentlyPlaying();
                    String currentStatus = "";
                    if (mRemoteMediaPlayer != null) {
                        mStatus = mRemoteMediaPlayer.getMediaStatus();
                        if (mStatus != null) {
                            currentStatus = "Player State: "
                                    + mStatus.getPlayerState() + "\n";
                            currentStatus += "Device " + mSelectedDevice.getFriendlyName() + "\n";
                            if (mStatus.getMediaInfo() != null) {
                                mInfo = mStatus.getMediaInfo();
                                mData = mInfo.getMetadata();
                                currentStatus += "Title "
                                        + mData.getString(MediaMetadata.KEY_TITLE) + "\n";
                            }

                            currentStatus += "Current Position: "
                                    + mStatus.getStreamPosition() + "\n";
                            currentStatus += "Approximate Stream Position: "
                                    + mRemoteMediaPlayer.getApproximateStreamPosition() + "\n";
                            currentStatus += "PlaybackRate: "
                                    + mStatus.getPlaybackRate() + "\n";
                            currentStatus += "Duration: "
                                    + mRemoteMediaPlayer.getStreamDuration() + "\n";
                            currentStatus += "Stream Volume set at: "
                                    + (mStatus.getStreamVolume() * 100) + "%\n";
                            currentStatus += "SYSTEM Volume set at: "
                                    + (Cast.CastApi.getVolume(mApiClient) * 100) + "%\n";
                            currentStatus += "streamType " + mInfo.getStreamType() + "\n";
                        } else {
                            mStatusText.setText("Stats not received");
                        }
                        mStatusText.setText(currentStatus);
                    } else {
                        mStatusText.setText("Not Casting");
                    }
                } catch (Exception e) {
                    Log.e(TAG, "Status request failed: " + e);
                }
            }
        });
    }

    /**
     * Updates a view with the title of the currently playing media.
     */
    protected void updateCurrentlyPlaying() {
        String playing = "";
        if (mRemoteMediaPlayer != null) {
            if (mData != null) {
                playing = "Media Selected: "
                        + mData.getString(MediaMetadata.KEY_TITLE);
                if (mRemoteMediaPlayer != null) {
                    String colorString = "<br><font color=#0066FF>";
                    colorString += "Casting to " + mSelectedDevice.getFriendlyName();
                    colorString += "</font>";
                    playing += colorString;
                }
                mCurrentlyPlaying.setText(Html.fromHtml(playing));
            } else {
                String castString = "<font color=#FF0000>";
                castString += getResources().getString(R.string.tap_to_select);
                castString += "</font>";
                mCurrentlyPlaying.setText(Html.fromHtml(castString));
            }
        } else {
            //Log.d(TAG, "Not Casting");
        }
    }

    /**
     * A Runnable class that updates a view to display status for the currently
     * playing media.
     */
    private class StatusRunner implements Runnable {
        @Override
        public void run() {
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    updateStatus();
                    Thread.sleep(1500);
                } catch (Exception e) {
                    Log.e(TAG, "Thread interrupted: " + e);
                }
            }
        }
    }

}
